#ifndef PARAFLUIDSPH_H
#define PARAFLUIDSPH_H

#include "parafluidatom.h"
#include "parafluidbox.h"
#include "parafluidmultihashmap.h"
#include "parafluidvector.h"

#include <QDataStream>

namespace ParaFluid
{
	
	/**
	* SPH
	*/
	class SPH
	{
		
	public:
		
		/**
		* Destructor
		*/
		~SPH();
		
		/**
		* Constructor with Members
		* @note Optimal number of Buckets is ~90% number of Atoms
		*/
		SPH(unsigned int buckets, float smoothingDistance, float timeStep, const Vector & gravity, const Box & constraints);
		
		/**
		* Serialize
		*/
		void serialize(QDataStream & stream) const;
		
		/**
		* Unserialize
		*/
		void unserialize(QDataStream & stream);
		
		/**
		* Getter for Smoothing Distance
		*/
		float smoothingDistance() const;
		
		/**
		* Getter for TimeStep
		*/
		float timeStep() const;
		
		/**
		* Getter for Last Step's TimeStep
		*/
		float lastTimeStep() const;
		
		/**
		* Getter for Gravity
		*/
		const Vector & gravity() const;
		
		/**
		* Getter for Constraints
		*/
		const Box & constraints() const;
		
		/**
		* Setter for Smoothing Distance
		*/
		void setSmoothingDistance(float value);
		
		/**
		* Setter for Time Step
		*/
		void setTimeStep(float value);
		
		/**
		* Setter for Gravity
		*/
		void setGravity(const Vector & value);
		
		/**
		* Setter for Constraints
		*/
		void setConstraints(const Box & value);
		
		/**
		* Remove All Atoms from the Simulation
		*/
		void clear();
		
		/**
		* Step in Time for a Time Step
		*/
		void step();
		
		void parallelStepMaster();
		
		void parallelStepSlave1(QDataStream & result, int indexFirst, int indexLast);
		
		void parallelStepSlave2(QDataStream & result, int indexFirst, int indexLast);
		
		void parallelStepSlave3(QDataStream & result, int indexFirst, int indexLast);
		
		void parallelSerializeAtom(QDataStream & stream, int indexFirst, int indexLast);
		
		void parallelUnserializeAtom(QDataStream & stream, int indexFirst, int indexLast);
		
		/**
		* Atoms
		*/
		AtomVoxelizer atomVoxelizer;
		
	protected:
		
		/**
		* Update Member variables to their new values
		*/
		void updateMembers();
		
	private:
		
		/**
		* Time Step
		*/
		float _currentTimeStep;
		
		/**
		* Gravity
		*/
		Vector _currentGravity;
		
		/**
		* Constraints
		*/
		Box _currentConstraints;
		
		/**
		* Next Value of Smoothing Distance
		*/
		float _newSmoothingDistance;
		
		/**
		* Next Value of Time Step
		*/
		float _newTimeStep;
		
		/**
		* Next Value for Gravity
		*/
		Vector _newGravity;
		
		/**
		* Next Value for Constraints
		*/
		Box _newConstraints;
		
		/**
		* Last Value for Time Step
		*/
		float _lastTimeStep;
		
		/**
		* Clear All Atoms
		*/
		bool _clear;
		
		/**
		* Default Constructor should not be used
		*/
		SPH();
		
		/**
		* Copy Constructor should not be used
		*/
		SPH(const SPH & other);
		
		/**
		* Assignment Operator should not be used
		*/
		SPH & operator= (const SPH & rhs);
		
	};
	
}

QDataStream & operator<< (QDataStream & stream, const ParaFluid::SPH & sph);

QDataStream & operator>> (QDataStream & stream, ParaFluid::SPH & sph);

#endif

